﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace theguineasyndrome
{
    public class ObstacleElement : LevelElement
    {
        public static float DEPTH = 30;
        public static float HALF_DEPTH = DEPTH / 2;

        public delegate void UpdaterFunc(ObstacleElement obstacle, GameTime gameTime);
        private UpdaterFunc updater;

        private List<Splash> topSplashes;

        protected Character character;
        protected Vector2 characterRelativePosition;

        public ObstacleElement(Level level, Texture2D texture, Vector2 position)
            : base(level, texture, position)
        {
            topSplashes = new List<Splash>();
            updater = null;
            character = null;
        }

        public void setUpdater(UpdaterFunc updater)
        {
            this.updater = updater;
        }

        public override bool isDynamic()
        {
            return updater != null;
        }

        public override void queueDrawing(Camera camera)
        {
            if (camera.isVisible(rectangle))
                level.queueObjectDrawing(this);
        }

        public Character Character
        {
            get { return character; }
        }

        public Vector2 CharacterRelativePosition
        {
            get { return characterRelativePosition; }
        }

        public float CollisionTop
        {
            get { return Top + HALF_DEPTH; }
        }

        public float CollisionBottom
        {
            get { return Bottom - HALF_DEPTH; }
        }

        public float CollisionHeight
        {
            get { return Height - DEPTH; }
        }

        public override void notifyCharacterOver(Character character)
        {
            this.character = character;
            this.characterRelativePosition = character.Position - Position;
        }

        public override void notifyCharacterAway()
        {
            character = null;
        }

        public Rectangle getCollisionRectangle()
        {
            return new Rectangle((int)Left, (int)CollisionTop, (int)Width, (int)(Height - DEPTH));
        }

        public virtual void repulseCharacterX(Character character, Rectangle intersection, ref bool moveLeft, ref bool moveRight)
        {
            Vector2 obstacleCenter = Center;
            Vector2 characterCenter = character.Center;
            Vector2 characterPosition = character.Position;

            if (intersection.Width < intersection.Height)
            {
                if (characterCenter.X < obstacleCenter.X) // move left
                {
                    character.setPosition(new Vector2(Left - character.Width / 2, characterPosition.Y));
                    moveLeft = true;
                }
                else // move right
                {
                    character.setPosition(new Vector2(Right + character.Width / 2, characterPosition.Y));
                    moveRight = true;
                }
                character.SpeedX = 0;
            }
        }

        public virtual void repulseCharacterY(Character character, Rectangle intersection, ref bool moveUp, ref bool moveDown)
        {
            Vector2 obstacleCenter = Center;
            Vector2 characterCenter = character.Center;
            Vector2 characterPosition = character.Position;

            if (intersection.Width > intersection.Height)
            {
                if (characterCenter.Y < obstacleCenter.Y) // move up
                {
                    character.setPosition(new Vector2(characterPosition.X, CollisionTop + 1));
                    moveUp = true;
                    character.setObstacleElement(this);
                }
                else // move down
                {
                    character.setPosition(new Vector2(characterPosition.X, CollisionBottom + character.Height + 1));
                    moveDown = true;
                }
                character.SpeedY = 0;
            }
        }

        public virtual void repulseLiquidDrop(LiquidDrop drop)
        {
            drop.setKillingObstacle(this);

            float distLeft = drop.X - Left;
            float distRight = Right - drop.X;
            float distTop = drop.Y - CollisionTop;
            float distBottom = CollisionBottom - drop.Y;

            if (distLeft < distRight && distLeft < distTop && distLeft < distBottom) // move left
            {
                drop.X = Left - drop.getRadius();
                drop.NextDropSpeed = new Vector2(-drop.SpeedX, drop.SpeedY);
            }
            else if (distRight < distTop && distRight < distBottom) // move right
            {
                drop.X = Right + drop.getRadius();
                drop.NextDropSpeed = new Vector2(-drop.SpeedX, drop.SpeedY);
            }
            else if (distTop < distBottom) // move up
            {
                drop.Y = CollisionTop - drop.getRadius();
                drop.NextDropSpeed = new Vector2(drop.SpeedX, -drop.SpeedY);
            }
            else // move down
            {
                drop.Y = CollisionBottom + drop.getRadius();
                drop.NextDropSpeed = new Vector2(drop.SpeedX, -drop.SpeedY);
            }
        }

        public void addTopSplash(Splash splash)
        {
            if (canSplashAt(splash.CenterX))
            {
                splash.setObstacle(this);
                topSplashes.Add(splash);
            }
        }

        public bool canSplashAt(float x)
        {
            bool canSplash = true;
            foreach (Splash splash in topSplashes)
            {
                if (Math.Abs(splash.CenterX - x) < 0.5)
                {
                    canSplash = false;
                    break;
                }
            }
            return canSplash;
        }

        public Splash getTopSplashAt(float minX, float maxX)
        {
            Splash foundSplash = null;
            foreach (Splash splash in topSplashes)
            {
                if (splash.Left <= maxX && minX <= splash.Right)
                {
                    foundSplash = splash;
                    break;
                }
            }
            return foundSplash;
        }

        public void removeSplashesAt(float minX, float maxX)
        {
            for (int i = topSplashes.Count - 1; i >= 0; i--)
            {
                Splash splash = topSplashes[i];
                if (splash.Left <= maxX && minX <= splash.Right)
                    topSplashes.RemoveAt(i);
            }
        }

        public override void Draw(SpriteBatch spriteBatch, Camera camera)
        {
            base.Draw(spriteBatch, camera);
            foreach (Splash splash in topSplashes)
                splash.Draw(spriteBatch, camera);
        }

        public override void Update(GameTime gameTime)
        {
            if (isDynamic())
                updater(this, gameTime);
        }

        public override Vector2 Position
        {
            //get { return base.Position; }

            set
            {
                base.Position = value;
                foreach (Splash splash in topSplashes)
                    splash.updatePosition();
            }
        }
    }
}
